define([
    'app',
    'json2',
    'json5',
    'clipboard',
    'jquery.format',
    'zTree-exhide',
    'ace-language-tools'
], function(app, JSON2, JSON5, Clipboard) {
    app.controller('MockEditorController', function($rootScope, $scope, $compile, $http, $location, $timeout, $routeParams,
            xDialog, xUtil, toastr) {
        var self = this;

        self.module = $routeParams.module;
        var hasPath = $routeParams.path ? true : false;
        self.fullPath = (hasPath ? '/' + self.module + '/' + $routeParams.path : '');

        self.fullScreen = false;
        self.fontSize = 13;
        var dataEditor = null;

        self.status = { changed: false, enabled: false };
        self.extname = '';
        self.textMock = '';
        self.canUndo = false;
        self.canRedo = false;
        self.toggleWrap = true;

        var zTreeObj = null;
        var selectedNode = null;
        self.filter = '';
        self.node = { name: '', root: false, leaf: false, parent: '', url: '', modified: null };

        const template = '@标题：<%=title%>\n@备注：\n@URL：<%=url%>\n@Method：post\n@Headers：\n{}\n@入参：\n<%=param%>\n@错误：\n@出参：';
        const templates = {
            '.json': _.template('/**\n' + template + '\n*/\n'),
            '.xml': _.template('<!--\n' + template + '\n-->\n')
        };

        var newCount = 1;
        var setting = {
            view: {
                selectedMulti: false,
                dblClickExpand: false,
                addHoverDom: function(treeId, treeNode) {
                    if (!$rootScope.sessionUser.role) {
                        return;
                    }
                    if (treeNode.editNameFlag || treeNode.leaf || jQuery('#addBtn_' + treeNode.tId).length > 0) {
                        return;
                    }
                    // add button
                    jQuery('#' + treeNode.tId + '_span').after('<span class="button add" id="addBtn_' + treeNode.tId
                            + '" title="新建" onfocus="this.blur();"></span>');
                    var jqBtnAdd = jQuery('#addBtn_' + treeNode.tId);
                    if (jqBtnAdd) {
                        jqBtnAdd.bind('click', function() {
                            function addTreeNode() {
                                treeNode.parent = getParentPath(treeNode);
                                xDialog.open().editMock({
                                    module: self.module,
                                    treeNode: treeNode,
                                    modifiable: true
                                }).then(function(node) {
                                    var nodes = jQuery.fn.zTree.getZTreeObj('mock-tree').addNodes(treeNode, {
                                        id: (100000 + newCount),
                                        pId: treeNode.id,
                                        name: node.name,
                                        leaf: node.leaf
                                    });
                                    zTreeObj.selectNode(nodes[0]);
                                    jQuery('#' + nodes[0].tId + '>a').trigger('click');
                                });
                            }

                            if (confirmSaveFile(addTreeNode)) {
                                addTreeNode();
                            }

                            return false;
                        });
                    }
                },
                removeHoverDom: function(treeId, treeNode) {
                    jQuery('#addBtn_' + treeNode.tId).unbind().remove();
                }
            },
            data: {
                simpleData: {
                    enable: false
                }
            },
            edit: {
                enable: true,
                removeTitle: '删除',
                showRemoveBtn: function(treeId, treeNode) {
                    return $rootScope.sessionUser.role == 'admin' && !treeNode.root;
                },
                renameTitle: '重命名',
                showRenameBtn: function(treeId, treeNode) {
                    return $rootScope.sessionUser.role && !treeNode.root;
                }
            },
            callback: {
                beforeDrag: function() {
                    return false;
                },
                beforeEditName: function(treeId, treeNode) {
                    var isSelfOrParent = false;
                    if (selectedNode) {
                        var treeNodePath = getNodePath(treeNode),
                            selectedNodePath = getNodePath(selectedNode);
                        if (new RegExp('^' + treeNodePath).test(selectedNodePath)) {
                            isSelfOrParent = true;
                        }
                    }

                    function editNodeName() {
                        treeNode.parent = getParentPath(treeNode);
                        xDialog.open().editMock({
                            module: self.module,
                            treeNode: treeNode,
                            modifiable: false
                        }).then(function(success) {
                            if (success) {
                                // update node
                                zTreeObj.updateNode(treeNode);
                                // update file url
                                if (isSelfOrParent) {
                                    self.node.parent = getParentPath(selectedNode);
                                    self.node.name = selectedNode.name;
                                    self.node.url = getNodePath(self.node);
                                    jQuery('#' + selectedNode.tId + '>a').trigger('click');
                                }
                            }
                        });
                    }

                    if (isSelfOrParent) {
                        if (confirmSaveFile(editNodeName)) {
                            editNodeName();
                        }
                    } else {
                        editNodeName();
                    }

                    return false;
                },
                beforeRemove: function(treeId, treeNode) {
                    var path = getParentPath(treeNode) + '/' + treeNode.name;
                    xDialog.confirm('确认信息', '您确定要删除【' + path + '】吗？').then(function(yes) {
                        if (yes) {
                            $http.post('/mock/data/delete', {
                                module: self.module,
                                path: path
                            }).then(function(response) {
                                if (response.data) {
                                    if (treeNode.leaf) {
                                        toastr.success('文件【' + treeNode.name + '】已删除！');
                                    } else {
                                        toastr.success('文件夹【' + treeNode.name + '】已删除！');
                                    }
                                    jQuery.fn.zTree.getZTreeObj('mock-tree').removeNode(treeNode);
                                    var nodePath = self.node.parent + '/' + self.node.name;
                                    if ((new RegExp('^' + path)).test(nodePath)) {
                                        self.status.changed = false;
                                        self.node.parent = '';
                                        self.node.name = '';
                                        self.node.url = '';
                                        self.textMock = '';
                                    }
                                }
                            });
                        }
                    });
                    return false;
                },
                beforeClick: function(treeId, treeNode) {
                    if (selectedNode && selectedNode.leaf) {
                        return confirmSaveFile(function() {
                            jQuery('#' + treeNode.tId + '>a').trigger('click');
                        });
                    }
                    return true;
                },
                onClick: function(event, treeId, treeNode) {
                    selectedNode = treeNode;
                    self.node.parent = getParentPath(selectedNode);
                    self.node.name = treeNode.name;
                    self.node.leaf = treeNode.leaf;
                    self.node.root = treeNode.root;

                    var path = getNodePath(self.node);
                    self.node.url = path;
                    if (!hasPath) {
                        $location.path('/mock-editor' + path, false).replace();
                    }
                    hasPath = false;

                    if (self.node.leaf) {
                        $http({
                            method: 'POST',
                            url: path + '?origin=true',
                            transformResponse: function(response) {
                                return response;
                            }
                        }).then(function(response) {
                            self.node.modified = response.headers('x-modified');
                            self.status.changed = false;
                            self.status.enabled = true;
                            self.textMock = response.data;
                            // change editor mode
                            self.extname = path.substring(path.lastIndexOf('.'));
                            var modeId = 'ace/mode/' + (self.extname == '.json' ? 'hjson' : 'xml');
                            if (modeId != dataEditor.getOption('mode')) {
                                dataEditor.getSession().setMode(modeId);
                            }
                            // set value
                            dataEditor.setValue(self.textMock, -1);
                            dataEditor.resize();
                            dataEditor.focus();
                            if (response.data == '{}' || response.data == '<xml/>') {
                                dataEditor.execCommand('documentComment');
                            } else {
                                self.gotoTop();
                            }
                            // undo & redo
                            dataEditor.getSession().getUndoManager().reset();
                            self.canUndo = false;
                            self.canRedo = false;
                        });
                    } else {
                        self.status.changed = false;
                        self.status.enabled = false;
                        self.textMock = '';
                        $timeout(function() { $scope.$apply(); });
                    }
                }
            }
        };

        self.init = function() {
            // editor language tools
            const snippets = _.map([
                { caption: '@Valid', snippet: '@Valid', score: 10000 },
                { caption: '@Null', snippet: '@Null', score: 9999 },
                { caption: '@NotNull', snippet: '@NotNull', score: 9998 },
                { caption: '@NotEmpty', snippet: '@NotEmpty', score: 9997 },
                { caption: '@NotBlank', snippet: '@NotBlank', score: 9996 },
                { caption: '@Length', snippet: '@Length(min = ${1:min}, max = ${2:max})${3}', score: 9995 },
                { caption: '@Min', snippet: '@Min(value = ${1:value})${2}', score: 9994 },
                { caption: '@Max', snippet: '@Max(value = ${1:value})${2}', score: 9993 },
                { caption: '@Size', snippet: '@Size(min = ${1:min}, max = ${2:max})${3}', score: 9992 },
                { caption: '@Range', snippet: '@Range(min = ${1:min}, max = ${2:max})${3}', score: 9991 },
                { caption: '@DecimalMin', snippet: '@DecimalMin(value = "${1:value}")${2}', score: 9990 },
                { caption: '@DecimalMax', snippet: '@DecimalMax(value = "${1:value}")${2}', score: 9989 },
                { caption: '@Past', snippet: '@Past', score: 9988 },
                { caption: '@Future', snippet: '@Future', score: 9987 },
                { caption: '@Pattern', snippet: '@Pattern(regexp = "${1:regexp}", flags = {${2:flags}})${3}', score: 9986 },
            ], function (snippet) {
                snippet['meta'] = 'validator';
                snippet['type'] = 'snippet';
                return snippet;
            });
            ace.require("ace/ext/language_tools").addCompleter({
                // validation-api @Annotation regexp
                identifierRegexps: [/@[a-zA-Z_0-9]*/],
                getCompletions: function(editor, session, pos, prefix, callback) {
                    if (/^@/.test(prefix)) {
                        return callback(null, snippets);
                    }
                    return callback(null, []);
                }
            });
            // data editor
            dataEditor = ace.edit('data-editor');
            dataEditor.setOptions({
                mode: 'ace/mode/hjson',
                fontSize: self.fontSize,
                wrap: self.toggleWrap,
                showPrintMargin: false,
                autoScrollEditorIntoView: true,
                enableBasicAutocompletion: true,
                enableSnippets: true,
                enableLiveAutocompletion: true
            });
            dataEditor.on('change', function(e) {
                // apply scope queue
                $timeout(function() {
                    self.status.changed = true;
                    if (dataEditor.getValue() == self.textMock) {
                        self.status.changed = false;
                    }
                    self.canUndo = dataEditor.getSession().getUndoManager().canUndo();
                    self.canRedo = dataEditor.getSession().getUndoManager().canRedo();
                });
            });
            dataEditor.getSession().on('changeAnnotation', function(e) {
                // check errors
                if (dataEditor.getValue() != self.textMock) {
                    var annotations = dataEditor.getSession().getAnnotations();
                    var canChanged = true;
                    if (!_.isEmpty(annotations)) {
                        if (_.find(annotations, function(annotation) {
                            return annotation.type == 'error';
                        })) {
                            canChanged = false;
                        }
                    }
                    if (self.status.changed != canChanged) {
                        self.status.changed = canChanged;
                        // apply scope queue
                        $timeout(function() { $scope.$apply(); });
                    }
                }
            });
            // find and replace
            dataEditor.commands.addCommand({
                name: 'findAndReplace',
                bindKey: { win: 'Ctrl-F', mac: 'Command-F' },
                exec: function(editor) {
                    self.showSearchbox();
                }
            });
            // document comment
            dataEditor.commands.addCommand({
                name: 'documentComment',
                bindKey: { win: 'Ctrl-H', mac: 'Command-H' },
                exec: function(editor) {
                    editor.gotoLine(1);
                    editor.insert(templates[self.extname]({
                        title: '',
                        url: getMockPath(),
                        param: self.extname == '.json' ? '{}' : '<xml/>'
                    }));
                    editor.moveCursorTo(1, 4);
                }
            });
            // upgrade document
            const keywords = ['标题', '备注', 'URL', 'Method', 'Headers', '入参', '错误', '出参'];
            dataEditor.commands.addCommand({
                name: 'upgradeDocument',
                bindKey: { win: 'Ctrl-U', mac: 'Command-U' },
                exec: function(editor) {
                    var docValue = editor.getValue();
                    _.forEach(keywords, function(keyword) {
                        var rkeyword = new RegExp('^(' + keyword + ')：', 'gmi');
                        docValue = docValue.replace(rkeyword, function(whole, match) {
                            return whole.replace(match, '@' + keyword);
                        });
                    });
                    editor.setValue(docValue, -1);
                    editor.resize();
                }
            });
            // refresh URL
            dataEditor.commands.addCommand({
                name: 'refreshUrl',
                bindKey: { win: 'Ctrl-R', mac: 'Command-R' },
                exec: function(editor) {
                    var docValue = editor.getValue()
                    docValue = docValue.replace(/^@URL：(.*)/gmi, function(whole, match) {
                        var params = match.replace(/^\/[^\?]+\??/g, '');
                        return '@URL：' + getMockPath() + (params ? '?' + params : '');
                    });
                    editor.setValue(docValue, -1);
                    editor.resize();
                }
            });
            // format code
            var formattedCode = '';
            dataEditor.commands.addCommand({
                name: 'format',
                bindKey: { win: 'Ctrl-Shift-F', mac: 'Command-Option-F' },
                exec: function(editor) {
                    if (editor.getValue() != formattedCode) {
                        if (self.extname == '.json') {
                            formattedCode = JSON2.format(editor.getValue(), '    ');
                        } else if (self.extname == '.xml') {
                            formattedCode = jQuery.format(editor.getValue(), { method: 'xml' });
                        }
                        editor.setValue(formattedCode, -1);
                        editor.resize();
                    }
                }
            });
            // goto line
            dataEditor.commands.addCommand({
                name: 'gotoline',
                bindKey: { win: 'Ctrl-L', mac: 'Command-L' },
                exec: function(editor) {
                    xDialog.open().showGotoLine({
                        maxLine: editor.session.getLength()
                    }).then(function(line) {
                        editor.gotoLine(line);
                    });
                }
            });
            // save
            dataEditor.commands.addCommand({
                name: 'save',
                bindKey: { win: 'Ctrl-S', mac: 'Command-S' },
                exec: function(editor) {
                    if (self.status.enabled && self.status.changed) {
                        self.save();
                    }
                }
            });
            // data editor tools
            jQuery('#data-editor .ace_scroller').append($compile(jQuery('#editor-tools').html())($scope));
            jQuery('.x-editor-tool').draggable({
                cursor: 'move',
                handle: '.x-toolbar',
                drag: function() {
                    jQuery(this).css('right', 'auto');
                }
            }).mousedown(function(e) {
                return false;
            });
            dataEditor.getSession().on('changeScrollTop', function(scrollTop) {
                if (scrollTop > 50) {
                    jQuery('.x-editor-tool.x-goto-top').removeClass('hidden');
                } else {
                    jQuery('.x-editor-tool.x-goto-top').addClass('hidden');
                }
            });
            // full screen
            $scope.$watch('vm.fullScreen', function() {
                setTimeout(function() {
                    dataEditor.resize();
                    jQuery('.x-editor-tool').css('left', '').css('top', '').css('right', '');
                }, 50);
            });

            new Clipboard('#copy', {
                text: function() {
                    return dataEditor.getValue();
                }
            }).on('success', function(e) {
                toastr.success('已复制到剪贴板！');
            });

            // load tree nodes
            loadTreeNodes();

            // ztree filter fixed
            jQuery('.x-tree').parents('.x-page-scope').scroll(function() {
                jQuery(this).find('.x-tree-filter').css('top', this.scrollTop + 'px')
                    .css('left', this.scrollLeft + 'px');
            });
        }

        self.collapseAll = function() {
            zTreeObj.expandAll(false);
        }

        self.expandAll = function() {
            zTreeObj.expandAll(true);
        }

        self.refresh = function() {
            function refreshNodes() {
                self.fullPath = $location.path().replace(/^\/mock-editor/, '');
                loadTreeNodes();
                toastr.success('文件列表已更新！');
            }
            if (confirmSaveFile(refreshNodes)) {
                refreshNodes();
            }
        }

        function confirmSaveFile(callback) {
            if (self.status.enabled && self.status.changed) {
                xDialog.confirm('确认信息', '文件数据未保存，您确定要离开吗？').then(function(yes) {
                    if (yes) {
                        self.status.changed = false;
                        callback();
                    } else {
                        dataEditor.focus();
                    }
                });
                return false;
            }
            return true;
        }

        function loadTreeNodes() {
            self.textMock = '';
            self.status.changed = false;
            self.status.enabled = false;
  
            self.filter = '';
            self.node.parent = '';
            self.node.name = '';
            self.node.root = false;
            self.node.leaf = false;
            
            jQuery.fn.zTree.destroy('mock-tree');
            $http.get('/mock/data/tree?module=' + self.module).then(function(response) {
                if (response.data === false) {
                    xDialog.alert('提示信息', '数据模块【' + self.module + '】不存在！');
                } else {
                    zTreeObj = jQuery.fn.zTree.init(jQuery('#mock-tree'), setting, response.data);
                    if (!_.isEmpty(self.fullPath)) {
                        var nodes = zTreeObj.getNodesByFilter(function(node) {
                            node.parent = getParentPath(node);
                            if(getNodePath(node) == self.fullPath) {
                                return true;
                            }
                            return false;
                        });
                        if (_.isEmpty(nodes)) {
                            var path = self.fullPath.replace(new RegExp('^/' + self.module), '');
                            if (path) {
                                xDialog.alert('提示信息', '文件路径【' + path + '】不存在！');
                            }
                        } else {
                            zTreeObj.selectNode(nodes[0]);
                            zTreeObj.expandNode(nodes[0], true, false, false);
                            jQuery('#' + nodes[0].tId + '>a').trigger('click');
                        }
                        self.fullPath = '';
                    }
                }
            });
        }

        function getParentPath(node) {
            var paths = node.getPath();
            var parent = '';
            for (var i = 1; i < paths.length - 1; i++) {
                parent += '/' + paths[i].name;
            }
            return parent;
        }

        function getNodePath(node) {
            var path = '/' + self.module + node.parent + '/' + node.name;
            if (!node.leaf) {
                path = (node.root ? '/' + self.module : path) + '/';
            }
            return path;
        }

        function getMockPath() {
            return (self.node.url == '' ? self.node.url :
                self.node.url.replace(new RegExp('^/' + self.module + '|\.(json|xml)$', 'gi'), ''));
        }

        self.save = function() {
            var title = xUtil.regex.subMatch(dataEditor.getValue(), /^@标题：(.*)/gm);
            if (/^\s*$/.test(title)) {
                toastr.error('文件【标题】不可以为空！');
                return;
            }
            xDialog.confirm('确认信息', '您确定要保存文件【' + self.node.name + '】的数据吗？').then(function(yes) {
                if (yes) {
                    $http.post('/mock/data/save', {
                        module: self.module,
                        type: 'file',
                        folder: self.node.parent,
                        file: self.node.name,
                        data: dataEditor.getValue(),
                        modified: self.node.modified
                    }).then(function(response) {
                        if (response.data == 'expired') {
                            xDialog.alert('提示信息', '文件数据已被更新，请刷新后再保存！');
                        } else if (response.data) {
                            self.node.modified = response.data;
                            toastr.success('文件数据已保存！');
                            self.status.changed = false;
                            self.textMock = dataEditor.getValue();
                        }
                    });
                }
            });
        }

        var showNodes = [];
        self.filterTree = function() {
            var allNode = zTreeObj.transformToArray(zTreeObj.getNodes());
            zTreeObj.hideNodes(allNode);
            if (self.filter == '') {
                zTreeObj.showNodes(allNode);
                zTreeObj.expandAll(true);
            } else {
                var rfilter = new RegExp(self.filter, 'i');
                showNodes = zTreeObj.getNodesByFilter(function(node) {
                    return !_.isEmpty(node.name.match(rfilter));
                });
                showNodes = zTreeObj.transformToArray(showNodes);
                for (var node in showNodes) {
                    if (showNodes.hasOwnProperty(node)) {
                        findParentNode(zTreeObj, showNodes[node]);
                    }
                }
                zTreeObj.showNodes(showNodes);
            }
        }

        function findParentNode(treeObj, node) {
            treeObj.expandNode(node, true, false, false);
            var parentNode = node.getParentNode();
            if (parentNode != null) {
                showNodes.push(parentNode);
                findParentNode(treeObj, parentNode);
            }
        }

        self.copyMock = function(type) {
            function copyMockData() {
                xDialog.open().copyMock({
                    type: type,
                    module: self.module,
                    node: self.node,
                    extname: self.extname
                }).then(function(data) {
                    self.fullPath = '/' + self.module + data.path;
                    loadTreeNodes();
                });
            }
            if (confirmSaveFile(copyMockData)) {
                copyMockData();
            }
        }

        self.handle = function(name, value) {
            switch (name) {
                case 'font':
                    self.fontSize = value;
                    dataEditor.setFontSize(value);
                    break;
                case 'edit':
                    xDialog.open().editMockDoc({ type: value, extname: self.extname, editor: dataEditor });
                    break;
                case 'undo':
                case 'redo':
                    dataEditor[name]();
                    break;
                case 'format':
                    dataEditor.execCommand('format');
                    toastr.success('文件数据已格式化！');
                    break;
                case 'wrap':
                    self.toggleWrap = dataEditor.getOption('wrap') == 'off' ? true : false;
                    dataEditor.setOption('wrap', self.toggleWrap);
                    break;
                case 'fold':
                    dataEditor.getSession().foldAll(1);
                    break;
                case 'unfold':
                    dataEditor.getSession().unfold();
                    break;
                default:
                    break;
            }
        }

        // show searchbox
        self.searchText = '';
        self.searchboxClosed = true;
        self.showSearchbox = function() {
            // open searchbox dialog
            if (self.searchboxClosed) {
                self.searchboxClosed = false;
                xDialog.open().showSearchbox({
                    scope: $scope,
                    editor: dataEditor,
                    context: self.searchText
                }).then(function(closed) {
                    self.searchboxClosed = true;
                }, function(cancelled) {
                    self.searchboxClosed = true;
                });
            }
            // load search text
            var selectedText = dataEditor.getSession().getTextRange(dataEditor.getSelectionRange());
            if (!_.isEmpty(selectedText)) {
                $scope.$apply(function() {
                    self.searchText = selectedText;
                });
            }
        }

        // show shortcut key
        self.showShortcutKey = function() {
            xDialog.open().showShortcutKey();
        }

        // goto top of editor
        self.gotoTop = function() {
            dataEditor.scrollToLine(0);
        }

        // show mock test
        self.showMockTest = function() {
            xDialog.open().testMock({
                path: self.node.url,
                extname: self.extname,
                data: dataEditor.getValue()
            });
        }
    });
});
